/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "../Relations.h"

template<class VecType>
class Box
{
public:
	VecType min, max;

	Box() {}
	Box(const VecType& mi, const VecType& ma) : min(mi), max(ma) {}

	VecType center() const
	{
		return min + (max-min).half();
	}

	RangeRelation
	testBox(const Box<VecType>& other) const;

	Box<VecType>
	getBounds() const
	{
		return *this;
	}
};

template<class VecType>
class BoxTests
{
public:
	static RangeRelation
	boxRelation(const Box<VecType>& b1, const Box<VecType>& b2)
	{
		RangeRelation relation=checkRangesSorted
					(b1.min.getComponent(0), b1.max.getComponent(0),
					 b2.min.getComponent(0), b2.max.getComponent(0));

		if(relation==Outside)
			return Outside;

		RangeRelation firstRelation=relation;
		bool bStillFirst=true;

		for(unsigned int iDim=1; iDim<VecType::dimension; iDim++)
		{
			relation=checkRangesSorted
					(b1.min.getComponent(iDim), b1.max.getComponent(iDim),
					 b2.min.getComponent(iDim), b2.max.getComponent(iDim));

			if(relation==Outside)
				return Outside;

			if(relation!=firstRelation)
				bStillFirst=false;
				//can't break here because outside has to be tested for each dim
		}
		if(bStillFirst)
			return firstRelation;
		//else
		return Overlaps;
	}

	static
	inline VecType
	getBoxVertex(const Box<VecType>& box, const unsigned int& iIndex)
	{
		vAssert(iIndex<VecType::childCount);
		VecType vRes;
		unsigned int mask=1 << (VecType::dimension-1);
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
		{
			vRes[iDim]= (iIndex & mask) ?
							box.max.getComponent(iDim) :
							box.min.getComponent(iDim);
			mask>>=1;
		}
		return vRes;
	}

	//return value: stuff inside canvas  or not
	static Box<VecType>
	canvasBox(const Box<VecType>& box, const Box<VecType>& canvas)
	{
		Box<VecType> out;
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
		{
			if(box.min.getComponent(iDim)<canvas.min.getComponent(iDim))
				out.min[iDim]=canvas.min.getComponent(iDim);
			else
				out.min[iDim]=box.min.getComponent(iDim);

			if(box.max.getComponent(iDim)>canvas.max.getComponent(iDim))
				out.max[iDim]=canvas.max.getComponent(iDim);
			else
				out.max[iDim]=box.max.getComponent(iDim);
		}
		return out;
	}

	inline static void
	box2Ext(const Box<VecType>& box, VecType& vPos, VecType& vExt)
	{
		vPos=(box.min+box.max).half();
		vExt=box.max-box.min;
	}

	inline static void
	ext2Box(const VecType& vPos, const VecType& vExt, Box<VecType>& box)
	{
		box.min=vPos-vExt.half();
		box.max=box.min+vExt;
	}


	//check if box has positive volume
	inline static bool
	boxValid(const Box<VecType>& box)
	{
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
			if(box.max.getComponent(iDim)<box.min.getComponent(iDim))
				return false;
		return true;
	}

	//check if box has positive volume
	inline static bool
	boxValid(const VecType& vExt)
	{
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
			if(vExt.getComponent(iDim)<0)
				return false;
		return true;
	}


	inline static int
	getQuadrant(const VecType& vBase, const VecType& v)
	{
		unsigned int ret=0;
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
		{
			int i=( (v.getComponent(iDim)-vBase.getComponent(iDim) ) >= 0) ? 1 : 0;
			ret= (ret<<1) | i;
		}
		return ret;
	}

	static inline bool
	isInsideBox(const Box<VecType>& box, const VecType& v)
	{
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
		{
			const typename VecType::scalarType iComponent=v.getComponent(iDim);
			if((iComponent < box.min.getComponent(iDim)) ||
				(iComponent > box.max.getComponent(iDim)))
				return false;
		}
		return true;
	}

	static Box<VecType>
	correctBox(const Box<VecType>& box)
	{
		Box<VecType> out;
		for (unsigned int iDim = 0; iDim < VecType::dimension; iDim++)
		{
			if(box.max.getComponent(iDim)>box.min.getComponent(iDim))
			{
				out.max[iDim]=box.max.getComponent(iDim);
				out.min[iDim]=box.min.getComponent(iDim);
			}
			else
			{
				out.min[iDim]=box.max.getComponent(iDim);
				out.max[iDim]=box.min.getComponent(iDim);
			}
		}
		return out;
	}
};

template <class VecType>
RangeRelation
Box<VecType>::testBox(const Box<VecType>& other) const
{
	return BoxTests<VecType>::boxRelation(other, *this);
}
